// const regeneratorRuntime = require('../../vendor/runtime.js');
const md5 = require('../md5.js');
const _ = require('../underscore.js');

const { getStorage, getGlobalData } = require('./ws-common-data-operation.js');
const { config } = require('./ws-config.js');

// TODO 说明，测试此框架是否work，一个方法是这个发起的请求链接和util发起的请求链接做比较。
// 此方法库的最重要目的是使用登录与授权 及 缓存key值

/*
  拼接符合后台框架所需的链接query string。
  @params controller, action, do，格式为 'wxapp/home/navs'
  @params querystring 格式为 {参数名1 : 值1, 参数名2 : 值2}
 */
function _spliceURL(action, querystring) {
  let url = `${config.siteroot}?i=${config.uniacid}&t=${config.multiid}&v=${config.version}&from=wxapp&m=${config.module}&divisionid=${config.divisionid}&`;
  if (config.cfgtime) url += `cfgtime=${config.cfgtime}&`;

  if (action) {
    action = action.split('/');
    if (action[0]) url += `c=${action[0]}&`;
    if (action[1]) url += `a=${action[1]}&`;
    if (action[2]) url += `do=${action[2]}&`;
  }
  if (querystring && typeof querystring === 'object') {
    for (let param in querystring) {
      if (param && querystring.hasOwnProperty(param) && querystring[param]) {
        url += `${param}=${querystring[param]}&`;
      }
    }
  }
  return url;
}

function _getSessionID() {
  // 兼容旧有的key值
  let sessionid = getGlobalData('wsSessionID') || getStorage('wsSessionID') || getStorage('userInfo').sessionid;
  return sessionid || '';
}

function _getFinalURL(option) {
  let sign;
  let state;
  let url = option.url;
  let timestamp = Date.parse(new Date());
  let sessionid = _getSessionID();

  option = option ? option : {};
  option.cachetime = option.cachetime || 0;

  if (url.indexOf('http://') === -1 && url.indexOf('https://') === -1) {
    url = _spliceURL(url);
  }

  state = _getUrlParamValue(url, 'state');
  // 此处sessionid必须来自于本地，否则时序混乱
  if (!state && !(option.data && option.data.state) && sessionid) {
    url += `&state=v8uuid-${sessionid}`;
  }

  // POST提交数据data不参加签名
  if (option.method === 'POST') sign = _getSign(url);
  else sign = _getSign(url, option.data);

  if (sign) url += `&sign=${sign}`;

  if (!url) return false;

  if (option.cachetime) {
    let cachekey = md5(url); // 最终链接（带有sign）的md5值作为缓存key。
    let cachedata = wx.getStorageSync(cachekey);

    if (cachedata && cachedata.data) {
      if (cachedata.expire > timestamp) {
        if (option.complete && typeof option.complete === 'function') {
          option.complete(cachedata);
        }
        if (option.success && typeof option.success === 'function') {
          option.success(cachedata);
        }
        console.log('get data from local cache:', cachedata);
        // wx.hideNavigationBarLoading();
        // 有缓存，中断请求，直接回调即可。
        return false;
      } else {
        wx.removeStorageSync(cachekey);
      }
    }
  }
  return url;
}

function getQuery(url) {
  let theRequest = [];
  if (url.indexOf('?') !== -1) {
    let str = url.split('?')[1];
    let strs = str.split('&');
    for (let i = 0; i < strs.length; i++) {
      if (strs[i].split('=')[0] && unescape(strs[i].split('=')[1])) {
        theRequest[i] = {
          name: strs[i].split('=')[0],
          value: unescape(strs[i].split('=')[1]),
        };
      }
    }
  }
  return theRequest;
}

/*
* 获取链接某个参数。 TODO，或许可以换个方式，不用正则的方式获取
* url 链接地址
* name 参数名称
*/
function _getUrlParamValue(url, name) {
  let reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)'); // 构造一个含有目标参数的正则表达式对象
  let r = url.split('?')[1].match(reg); // 匹配目标参数
  if (r != null) return unescape(r[2]);
  else return null; // 返回参数值
}

/**
 * 获取签名 将链接地址的所有参数按字母排序后拼接加上token进行md5
 * url 链接地址
 * date 参数{参数名1 : 值1, 参数名2 : 值2} *
 * token 签名token 非必须
 */
function _getSign(url, data, token) {
  let sign = _getUrlParamValue(url, 'sign');
  let querystring = '';
  let urlData = '';

  if (sign || (data && data.sign)) {
    return false;
  } else {
    if (url) {
      querystring = getQuery(url);
    }
    if (data) {
      let theRequest = [];
      for (let param in data) {
        if (param && data[param]) {
          theRequest = theRequest.concat({
            name: param,
            value: data[param],
          });
        }
      }
      querystring = querystring.concat(theRequest);
    }
    // 排序
    querystring = _.sortBy(querystring, 'name');
    // 去重
    querystring = _.uniq(querystring, true, 'name');
    for (let i = 0; i < querystring.length; i++) {
      if (querystring[i] && querystring[i].name && querystring[i].value && querystring[i].value !== '0') {
        urlData += querystring[i].name + '=' + querystring[i].value;
        if (i <= querystring.length - 1) {
          urlData += '&';
        }
      }
    }
    token = token ? token : config.token;
    sign = md5(urlData + token);
    return sign;
  }
}

/**
  简单封装wx.request函数，增加交互体全、配置缓存、以及配合后端格式化返回数据
  @params option 弹出参数表，
  {
    url : 同微信,
    data : 同微信,
    header : 同微信,
    method : 同微信,
    success : 同微信,
    fail : 同微信,
    complete : 同微信,
    cachetime : 缓存周期，在此周期内不重复请求http，默认不缓存
  }
*/
function request(option) {
  let url = _getFinalURL(option);
  let timestamp = Date.parse(new Date());
  let cachekey = md5(url);

  // TODO，改成promise执行，配合SDK使用，reject41009后重新走一遍相关流程即可
  return new Promise((resolve, reject) => {
    url && wx.request({
      url: url,
      data: option.data || {},
      header: option.header || {
        'content-type': 'application/x-www-form-urlencoded',
      },
      method: option.method || 'GET',
      success: function (response) {
        if (response.data.errno && Number(response.data.errno) !== 1) {
          if (Number(response.data.errno) === 41009) {
            // 接口返回未登录状态
            // 不同的模块不同的key值，其实此处调用SDK重新走一遍login即可
            // wx.setStorageSync('userInfo', '');

            // 不同模块操作/函数使用的不同key值和数据格式；均 setStorageSync userInfo
            // memberInfo: 管理后台框架自建的公共/通用表信息
            // access_token： userInfo: { sessionid: '', wxInfo: { openid: '', xx: '' } }  也有memberInfo，但是原util里调用中不会设置该值
            // getUserInfo： userInfo: { sessionid: '', wxInfo: { openid: '', xx: '' }, memberInfo: {} }
            // _wxLogin： userInfo: { sessionid： '', openid: '' }

            // util.request(option);
            //
            // 修改方案：
            // 兼容各级所有key值
            //
            // 如果是dologin或者getUserInfo类接口，应该是在SDK里统一处理并设置缓存
            reject(response);
            return;
          } else {
            if (response.data.message) {
              let redirect;
              if (response.data.data != null && response.data.data.redirect) {
                redirect = response.data.data.redirect;
              } else {
                redirect = '';
              }
              // TODO，handle error
              // message(response.data.message, redirect, 'error');
            }
            reject(response);
            return;
          }
        } else {
          // 写入缓存，减少HTTP请求，并且如果网络异常可以读取缓存数据
          if (option.cachetime) {
            let cachedata = { data: response.data, expire: timestamp + option.cachetime * 1000 };
            wx.setStorageSync(cachekey, cachedata);
          }
          resolve(response);
        }
      },
      fail: function (response) {
        // 如果请求失败，尝试从缓存中读取数据
        let cachedata = wx.getStorageSync(cachekey);
        if (cachedata && cachedata.data) {
          resolve(cachedata);
        } else {
          reject(response);
        }
      },
    });
  });
}

export {
  request
};
